package com.google.code.kjug.guava;

import com.google.common.base.*;
import com.google.common.collect.*;
import com.sun.org.apache.xpath.internal.operations.Equals;
import org.junit.Test;

import java.util.*;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

public class GuavaCollections_UT {

    @Test
    public void howDoIMakeAListOfStrings() {
        assertEquals(new ArrayList<String>(), Lists.<String>newArrayList());
    }

    @Test
    public void howDoIMakeASetOfStrings() {
        assertEquals(new HashSet<String>(), Sets.<String>newHashSet());
    }

    @Test
    public void howDoIMakeAMap() {
        assertEquals(new HashMap<String, String>(), Maps.newHashMap());
    }

    @Test
    public void howDoIMakeAListOfStringsWithStuffInIt() {
        ArrayList<String> listOfStrings = new ArrayList<String>();
        listOfStrings.add("a");
        listOfStrings.add("b");
        listOfStrings.add("c");

        ArrayList<String> easyArrayList = Lists.newArrayList("a", "b", "c");

        assertEquals(listOfStrings, easyArrayList);
    }

    @Test
    public void heyJoinerMakesThisSoFunAndEasy() {
        List<String> listOfStrings = Lists.newArrayList("alpha", "beta", null, "gamma", "delta");

        String joinedString = Joiner.on(", ").skipNulls().join(listOfStrings);  // how do you like fluent interfaces?

        assertEquals("alpha, beta, gamma, delta", joinedString);
    }

    @Test
    public void splitterMakesThingsReallyEasyToo() {
        String string = "          alpha,           beta,  gamma,    delta          ";  //big ole nasty string

        Iterable<String> words = Splitter.on(", ").trimResults().split(string);

        Iterator<String> iterator = words.iterator();
        assertEquals("alpha", iterator.next());
        assertEquals("beta", iterator.next());
        assertEquals("gamma", iterator.next());
        assertEquals("delta", iterator.next());
    }

    @Test
    public void lookHowEasyItIsToReverseThisList() {
        List<String> strings = Lists.newArrayList("alpha", "beta", "gamma", "delta");

        List<String> reversedStrings = Lists.reverse(strings);

        assertEquals(reversedStrings.get(0), "delta");
        assertEquals(reversedStrings.get(1), "gamma");
        assertEquals(reversedStrings.get(2), "beta");
        assertEquals(reversedStrings.get(3), "alpha");
    }

    @Test
    public void theReversedListIsStillTheSameList() {
        List<String> strings = Lists.newArrayList("alpha", "beta", "gamma", "delta");

        List<String> reversedStrings = Lists.reverse(strings);

        reversedStrings.remove(0);
        reversedStrings.remove(2);
        assertEquals(strings.get(0), "beta");
        assertEquals(strings.get(1), "gamma");
    }

    @Test
    public void lookHowEasyItIsToSortThisList() {
        List<String> strings = Lists.newArrayList("beta", "delta", "alpha", "gamma");

        List<String> sortedStrings = Ordering.natural().sortedCopy(strings);

        assertEquals("alpha", sortedStrings.get(0));
        assertEquals("beta", sortedStrings.get(1));
        assertEquals("delta", sortedStrings.get(2));
        assertEquals("gamma", sortedStrings.get(3));
    }

    @Test
    public void sortMyBankAccountsByNameThenDollarAmount() {
        BankAccount myBankAccount1 = new BankAccount(11, "Mine");
        BankAccount myBankAccount2 = new BankAccount(5, "Mine");
        BankAccount yourBankAccount = new BankAccount(10, "Yours");
        List<BankAccount> bankAccounts = Lists.newArrayList(myBankAccount1, myBankAccount2, yourBankAccount);

        Comparator<BankAccount> balanceComparator = new Comparator<BankAccount>() {
            public int compare(BankAccount o1, BankAccount o2) {
                Integer balance1 = new Integer(o1.getBalance());
                Integer balance2 = new Integer(o2.getBalance());
                return balance1.compareTo(balance2);
            }
        };
        Comparator<BankAccount> nameComparator = new Comparator<BankAccount>() {
            public int compare(BankAccount o1, BankAccount o2) {
                return o1.getName().compareTo(o2.getName());
            }
        };
        List<BankAccount> sortedBankAccounts = Ordering.compound(Lists.newArrayList(nameComparator, balanceComparator)).sortedCopy(bankAccounts);

        assertEquals(myBankAccount2, sortedBankAccounts.get(0));
        assertEquals(myBankAccount1, sortedBankAccounts.get(1));
        assertEquals(yourBankAccount, sortedBankAccounts.get(2));
    }

    @Test
    public void findMeAllOfTheBankAccountsWithMoreThanFiveDollars() {
        BankAccount myBankAccount1 = new BankAccount(11, "Yours");
        BankAccount myBankAccount2 = new BankAccount(2, "Yours");
        BankAccount yourBankAccount = new BankAccount(28, "Yours");
        ArrayList<BankAccount> bankAccounts = Lists.newArrayList(myBankAccount1, myBankAccount2, yourBankAccount);

        Predicate<BankAccount> substantialAccountFilter = new Predicate<BankAccount>() {
            public boolean apply(BankAccount bankAccount) {
                return bankAccount.getBalance() > 5;
            }
        };
        Collection<BankAccount> substantialAccounts = Collections2.filter(bankAccounts, substantialAccountFilter);

        assertEquals(2, substantialAccounts.size());
        Iterator<BankAccount> iterator = substantialAccounts.iterator();
        assertEquals(myBankAccount1, iterator.next());
        assertEquals(yourBankAccount, iterator.next());
    }

    @Test
    public void convertAllAccountsToYen() {
        BankAccount myBankAccount1 = new BankAccount(11, "Mine");
        BankAccount myBankAccount2 = new BankAccount(2, "Mine");
        BankAccount yourBankAccount = new BankAccount(28, "Yours");
        ArrayList<BankAccount> bankAccounts = Lists.newArrayList(myBankAccount1, myBankAccount2, yourBankAccount);

        Function<BankAccount, String> accountViewFunction = new Function<BankAccount, String>() {
            public String apply(BankAccount bankAccount) {
                return String.format("%s has %s dollars", bankAccount.getName(), bankAccount.getBalance());
            }
        };
        Collection<String> transformedAccounts = Collections2.transform(bankAccounts, accountViewFunction);

        Iterator<String> iterator = transformedAccounts.iterator();
        assertEquals("Mine has 11 dollars", iterator.next());
        assertEquals("Mine has 2 dollars", iterator.next());
        assertEquals("Yours has 28 dollars", iterator.next());
    }

    @Test
    public void multiMapsMakeItEasy() {
        Multimap<Integer, String> multimap = ArrayListMultimap.create();
        multimap.put(1, "one");
        multimap.put(1, "uno");
        multimap.put(1, "ein");
        multimap.put(2, "two");

        assertEquals("[one, uno, ein]", multimap.get(1).toString());
        assertEquals("[two]", multimap.get(2).toString());
    }

    @Test
    public void willItEventCategorizeForMe() {
        BankAccount okayBankAcount = new BankAccount(1, "Mine");
        BankAccount okayBankAcount1 = new BankAccount(1, "Mine");
        BankAccount okayBankAcount2 = new BankAccount(1, "Mine");
        BankAccount greatBankAccount = new BankAccount(50, "Yours");
        BankAccount greatBankAccount1 = new BankAccount(50, "Yours");
        BankAccount awesomeBankAcocunt = new BankAccount(1000, "Someone");
        ArrayList<BankAccount> bankAccounts
                = Lists.newArrayList(okayBankAcount, okayBankAcount1, okayBankAcount2, greatBankAccount, greatBankAccount1, awesomeBankAcocunt);

        Function<BankAccount, String> categoryFunction = new Function<BankAccount, String>() {
            public String apply(BankAccount input) {
                if (input.getBalance() > 10) {
                    return "great";
                } else if (input.getBalance() > 100) {
                    return "awesome";
                } else {
                    return "okay";
                }
            }
        };
        ImmutableListMultimap<String, BankAccount> indexedBankAccounts = Multimaps.index(bankAccounts, categoryFunction);

        assertEquals(3, indexedBankAccounts.get("okay").size());
        assertEquals(2, indexedBankAccounts.get("great").size());
        assertEquals(1, indexedBankAccounts.get("awesome").size());
    }

    private class BankAccount {
        private int balance;
        private String name;

        public BankAccount(int initialDeposit, String name) {
            balance = initialDeposit;
            this.name = name;
        }

        public int getBalance() {
            return balance;
        }

        public void setBalance(int balance) {
            this.balance = balance;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            BankAccount that = (BankAccount) o;

            return Objects.equal(that.getName(), this.getName());
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(this.getName());
        }
    }

}
